local class = flower.class

local Player = class(flower.SheetImage)

Player.DIRS = {}
Player.DIRS.DOWN  = "D"
Player.DIRS.UP    = "U"
Player.DIRS.LEFT  = "L"
Player.DIRS.RIGHT = "R"

Player.width = 20
Player.height = 20

function Player:init(tileMap)
  x,y = Player.getStartPos(tileMap)
  x = x or 0
  y = y or 0
  self.tileMap = tileMap
  self.onwall = "bottom"

  flower.SheetImage.init(self, '../assets/gfx/player.png', 60, 20)
  self:setTileSize(Player.width,Player.height)
  self:setPiv(10,11)
  self:setPos(x,y)

  self:createCollisionMap(tileMap)

  flower.InputMgr:addEventListener(flower.Event.KEY_DOWN, function(e) self:onPower(e) end)
      
  triangleMoveLeft = MOAIEaseDriver.new()
  
  triangleMoveLeft:reserveLinks(2)
  triangleMoveLeft:setLink(1,self,MOAIProp2D.ATTR_X_LOC, -20)
  triangleMoveLeft:setLink(2,self,MOAIProp2D.ATTR_Z_ROT, -120)
  triangleMoveLeft:setSpan(.2)
  
  triangleMoveRight = MOAIEaseDriver.new()
  
  triangleMoveRight:reserveLinks(2)
  triangleMoveRight:setLink(1,self,MOAIProp2D.ATTR_X_LOC, 20)
  triangleMoveRight:setLink(2,self,MOAIProp2D.ATTR_Z_ROT, 120)
  triangleMoveRight:setSpan(.2)

  triangleMoveUp = MOAIEaseDriver.new()
  
  triangleMoveUp:reserveLinks(2)
  triangleMoveUp:setLink(1,self,MOAIProp2D.ATTR_Y_LOC, 20)
  triangleMoveUp:setLink(2,self,MOAIProp2D.ATTR_Z_ROT, -120)
  triangleMoveUp:setSpan(.2)
  
  triangleMoveDown = MOAIEaseDriver.new()
  
  triangleMoveDown:reserveLinks(2)
  triangleMoveDown:setLink(1,self,MOAIProp2D.ATTR_Y_LOC, -20)
  triangleMoveDown:setLink(2,self,MOAIProp2D.ATTR_Z_ROT, 120)
  triangleMoveDown:setSpan(.2)
  
  MOAICoroutine.blockOnAction(triangleMoveLeft)
  MOAICoroutine.blockOnAction(triangleMoveRight)
  MOAICoroutine.blockOnAction(triangleMoveUp)
  MOAICoroutine.blockOnAction(triangleMoveDown)
  
end

function Player:destroy()
	flower.InputMgr:removeEventListener(flower.Event.KEY_DOWN, function(e) self:onPower(e) end)
end

function Player:onPower(e)
	if e.key == 32 then
		self:setIndex(math.max((self:getIndex()+1)%4, 1))
    --triange --> self:getIndex == 1
    --square --> self:getIndex == 2
    --circle --> self:getIndex == 3
	end
end

function Player:update()
    local currShape = self:getCurrentShape()

    local canMoveLeftRight = currShape ~= "triangle" or self.onwall == "bottom" or self.onwall == "top"
    local canMoveUpDown = currShape ~= "triangle" or self.onwall == "left" or self.onwall == "right"

    local cx,cy = self:getCollisionMapIndex()
    --printf("current collision map index (%d,%d), wall %s\n",cx,cy, self.onwall)
    local canMoveLeft,canMoveRight = canMoveLeftRight,canMoveLeftRight
    local canMoveUp,canMoveDown = canMoveUpDown,canMoveUpDown

    if (currShape == "triangle" and collisionMap) then
      if (self.onwall == "top" or self.onwall == "bottom") then
        local leftCell = collisionMap[cx][math.max(cy-1,1)]
        local rightCell = collisionMap[cx][math.min(cy+1,self.tileMap.mapWidth)]

        canMoveLeft = leftCell[Player.DIRS.UP] or leftCell[Player.DIRS.DOWN]
        canMoveRight = rightCell[Player.DIRS.UP] or rightCell[Player.DIRS.DOWN]
      elseif (self.onwall == "left" or self.onwall == "right") then
        local upCell   = collisionMap[math.max(cx,1)][cy]
        local downCell = collisionMap[math.min(cx,self.tileMap.mapHeight)][cy]

        canMoveUp   = upCell[Player.DIRS.LEFT] or upCell[Player.DIRS.RIGHT]
        canMoveDown = downCell[Player.DIRS.LEFT] or downCell[Player.DIRS.RIGHT]
      end
    end

    -- handle movement
  	if (flower.InputMgr:keyIsDown(100) or flower.InputMgr:keyIsDown(68)) then -- move right ("d" or "D")
      if (self:hitRightWall()) then
        self.onwall = "right"
      end

      if canMoveRight then
        if currShape == "triangle" then
            triangleMoveRight:start()
            --self:moveRot(-120,0)
        else
          self:moveLoc(1,0)
        end
      end
    elseif (flower.InputMgr:keyIsDown(97) or flower.InputMgr:keyIsDown(65)) then -- move left ("a" or "A")
      if (self:hitLeftWall()) then
        self.onwall = "left"
      end

      if canMoveLeft then
    		if currShape == "triangle" then
          triangleMoveLeft:start()
          --self:moveRot(120,0)
        else
          self:moveLoc(-1,0)
        end
      end
    elseif (flower.InputMgr:keyIsDown(119) or flower.InputMgr:keyIsDown(87)) then -- move up ("w" or "W")
      if (self:hitTopWall()) then
        self.onwall = "top"
      end

      if canMoveUp then
        if currShape == "triangle" then
          self:moveLoc(0,-1)
        else
          self:moveLoc(0,-1)
        end
      end
    elseif (flower.InputMgr:keyIsDown(115) or flower.InputMgr:keyIsDown(83)) then -- move down ("s" or "S")
      if (self:hitBottomWall()) then
        self.onwall = "bottom"
      end

      if canMoveDown then
        if currShape == "triangle" then
          self:moveLoc(0,1)
        else
          self:moveLoc(0,1)
        end
      end
	  end

    -- Check for wall hits and make changes if necessary

    -- Correct position based on collision detection
    local x,y = self:getPos()
    local minX, minY, maxX, maxY = self:getCurrentTileBounds()

    local newX = math.max(x,minX)
    newX = math.min(newX,maxX)
    local newY = math.max(y,minY)
    newY = math.min(newY,maxY)

    if (newX ~= x or newY ~= y) then
      --printf("old x,y (%f,%f), bounds (%f,%f,%f,%f), new (%f,%f)\n", x,y, minX, minY, maxX, maxY, newX, newY)
      self:setPos(newX,newY)
    end
end

function Player.getStartPos(tileMap)
    dummyLayer = tileMap:findMapLayerByName("pLayer")
    j = 0
    for i, tile in ipairs(dummyLayer.data.data) do
      j = j + 1
      if tile == 18 then
        px = (j%tileMap.mapWidth-1)
        py = math.floor(j/tileMap.mapWidth)
      end
    end
    return px*tileMap.tileWidth, py*tileMap.tileHeight
end

function Player:createCollisionMap(tileMap)
  wallTileData = tileMap:findMapLayerByName("walLayer").data
  collisionMap = {}
  local w = wallTileData.width
  for r = 1,wallTileData.height do
    local row = {}
    for c = 1,w do
      local collStr = tileMap:getTileProperty(wallTileData.data[w*(r-1) + c], "walls")
      local wallDirs = {}
      if (nil ~= collStr) then
        for c in collStr:gmatch"." do
          wallDirs[c] = true
        end
      end
      flower.table.insertElement(row, wallDirs)
    end
    flower.table.insertElement(collisionMap, row)
  end

  self.collisionMap = collisionMap
end

function Player:getCollisionMapIndex()
  local x,y = self:getPos()
  x = x + Player.width/2
  y = y + Player.height/2
  local tw,th = self.tileMap.tileWidth, self.tileMap.tileHeight
  return math.floor((y/th)+1), math.floor((x/tw)+1)
end

function Player:getCurrentTileBounds()
  local x,y = self:getPos()
  local cx,cy = self:getCollisionMapIndex()
  local minX, minY = 0,0
  local maxX, maxY = self.tileMap.tileWidth*self.tileMap.mapWidth, self.tileMap.tileHeight*self.tileMap.mapHeight
  local th = self.tileMap.tileHeight

  if (collisionMap[cx][cy][Player.DIRS.DOWN]) then
    maxY = th*cx
  end
  if (collisionMap[cx][cy][Player.DIRS.UP]) then
    minY = th*(cx-1)
  end
  if (collisionMap[cx][cy][Player.DIRS.RIGHT]) then
    maxX = self.tileMap.tileWidth*cy - Player.width
  end
  if (collisionMap[cx][cy][Player.DIRS.LEFT]) then
    minX = self.tileMap.tileWidth*(cy-1);
  end

  return minX, minY, maxX, maxY
end

function Player:hitRightWall()
  local x,y = self:getPos()
  local minX, minY, maxX, maxY = self:getCurrentTileBounds()

  return math.ceil(x) >= maxX
end

function Player:hitLeftWall()
  local x,y = self:getPos()
  local minX, minY, maxX, maxY = self:getCurrentTileBounds()

  return math.floor(x) <= minX
end

function Player:hitTopWall()
  local x,y = self:getPos()
  local minX, minY, maxX, maxY = self:getCurrentTileBounds()

  return math.floor(y) <= minY
end

function Player:hitBottomWall()
  local x,y = self:getPos()
  local minX, minY, maxX, maxY = self:getCurrentTileBounds()

  return math.ceil(y) >= maxY
end

function Player:getCurrentShape()
  local idx = self:getIndex()
  if (idx == 1) then return "triangle" end
  if (idx == 2) then return "square" end
  if (idx == 3) then return "circle" end
end

-- HELPERS --
function printTileDirs(cx,cy)
  local str = '(' .. cx .. ',' .. cy .. '): '
  if (collisionMap[cx][cy]) then
    for dir,v in pairs(collisionMap[cx][cy]) do
      str = str .. dir .. ','
    end
  else 
    str = str .. '<does not exist>'
  end

  return str
end

return Player
